<?php

namespace service\express\HttpRequester;

use service\express\Contracts\HttpRequestContract;
use service\express\Exceptions\HttpRequestException;

class Curl implements HttpRequestContract
{
    /**
     * errors
     *
     * @var string|array
     */
    protected $errors;

    /**
     * origin return
     *
     * @var mixed
     */
    protected $origin_return = null;

    /**
     * last requst infos
     *
     * @var array
     */
    protected $last_curl_info;

    /**
     * request header
     *
     * @var array
     */
    protected $headers = [
        "cache-control: no-cache",
        "Pragma: no-cache",
    ];

    /**
     * cURL Options
     *
     * @var array
     */
    protected $curl_options = [];

    /**
     * time_out
     *
     * @var integer
     */
    protected $time_out = 5;

    /**
     * set_option
     *
     * @param mixed $key
     * @param mixed $val
     * @return void
     */
    public function setOption($key, $val = null){
        if (is_array($key)) {
            $this->curl_options = array_merge($this->curl_options, $key);
        }elseif(is_int($key) && $val !== null){
            $this->curl_options[$key] = $val;
        }
    }

    /**
     * set http request header
     *
     * @param array $header
     * @return void
     */
    public function setRequestHeader($header)
    {
        if(is_array($header)){
            $this->headers = array_merge($this->headers, $header);
        }elseif(is_string($header)){
            $this->headers[] = $header;
        }
    }

    /**
     * curl_setopt_array
     *
     * @param  resource &$ch
     * @param  array $curl_options
     * @param  boolean $skip_error 设置错误时是否跳过
     * @return boolean
     */
    private function curl_setopt_array(&$ch, array $curl_options, $skip_error = true) {
        foreach ($curl_options as $option => $value) {
            if (!curl_setopt($ch, $option, $value) && !$skip_error) {
                return false;
            }
        }
        return true;
    }

    /**
     * get
     *
     * @param  string $url
     * @param  array  $options
     * @return mixed
     */
    public function get($url, array $options = [])
    {
        return $this->request($url, null, 'get', $options);
    }

    /**
     * post
     *
     * @param  string $url
     * @param  string|null|array $data
     * @param  array  $options
     * @return mixed
     */
    public function post($url, $data = null, array $options = [])
    {
        return $this->request($url, $data, 'post', $options);
    }

    /**
     * request
     *
     * @param  string $url
     * @param  string $method
     * @param  string|null|array $data
     * @param  array  $options
     * @return mixed
     */
    public function request($url, $data = null, $method = 'get', array $options = [])
    {
        if ($url === '') {
            return false;
        }
        $ch = curl_init();
        $curl_options = [
            CURLOPT_URL =>  $url,
            CURLOPT_HTTPHEADER =>  $this->headers,
            CURLOPT_CUSTOMREQUEST =>  strtoupper($method),
            CURLOPT_TIMEOUT =>  $this->time_out,
            CURLOPT_USERAGENT =>  'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML =>  like Gecko) Chrome/54.0.2403.125 Safari/537.36',
            CURLOPT_AUTOREFERER =>  true,
            CURLOPT_RETURNTRANSFER =>  true,
            CURLOPT_HEADER =>  false,
            CURLOPT_SSL_VERIFYPEER =>  false,
            CURLOPT_SSL_VERIFYHOST =>  false,
            CURLOPT_AUTOREFERER => true,
            CURLOPT_FOLLOWLOCATION =>  true,
        ];
        if ($data) {
            if (strtolower($method) === 'post') {
                $curl_options[CURLOPT_POSTFIELDS] = $data;
            } elseif(is_array($data)) {
                $url .= (stripos($url, '?') === false ? '?' : '&') . http_build_query($data);
                $curl_options[CURLOPT_URL] = $url;
            }
        }
        if(is_array($this->curl_options) && count($this->curl_options)){
            $curl_options = array_replace($curl_options, $this->curl_options);
        }
        is_array($options) && count($options) && $curl_options = array_replace($curl_options, $options);
        if(!curl_setopt_array($ch, $curl_options)){
             if(function_exists('curl_reset')){
                curl_reset($ch);
             }else{
                curl_close($ch);
                $ch = curl_init();
             }
            $this->curl_setopt_array($ch, $curl_options);
        }
        $this->origin_return = curl_exec($ch);
        $this->last_curl_info = curl_getinfo($ch);
        if ($this->origin_return === false) {
            $this->setError(curl_error($ch), 'error_msg');
            $this->setError(curl_errno($ch), 'error_code');
            $this->setError($this->last_curl_info , 'curl_info');
            $this->setError($this->origin_return , 'origin_return');
            curl_close($ch);
            return false;
        }
        curl_close($ch);
        return $this->origin_return;
    }

    /**
     * getError
     *
     * @return mixed
     */
    public function getError()
    {
        return $this->errors;
    }

    /**
     * get last request original return
     *
     * @return array
     */
    public function getLastRequestInfo()
    {
        return [
            $this->origin_return,
            $this->last_curl_info,
        ];
    }

    /**
     * getUserAgent
     *
     * @param  string $type  mobile|pc|all
     * @return string
     */
    public static function getUserAgent($type = 'all')
    {
        switch ($type) {
            case 'mobile':
                $user_agents = [
                    'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
                    // iPad
                    'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
                    // Galaxy S5
                    'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.23 Mobile Safari/537.36',
                    // Nexus 5X
                    'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.23 Mobile Safari/537.36',
                    // iPod
                    'Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3A101a Safari/419.3',
                ];
                break;
            case 'all':
            case 'pc':
            default:
                $user_agents = [
                    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393',
                    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',
                    'Opera/9.80 (Windows NT 6.1; WOW64; U; en) Presto/2.10.229 Version/11.62',
                    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2859.0 Safari/537.36',
                ];
                break;
        }
        return $user_agents[array_rand($user_agents)];
    }

    /**
     * setError
     *
     * @param mixed $error
     * @param string|null $key
     */
    protected function setError($error, $key = null)
    {
         if(!is_scalar($key)){
             $key = date('Y_m_d_H_i_s').mt_rand(1000, 9999);
         }
         $this->errors[(string)$key] = $error;
    }

    /**
     * throw a HttpRequest Exception
     *
     * @param  string  $msg
     * @param  integer $code
     * @param  throwable|null $previous
     * @return void
     */
    protected static function throwException($msg, $code = 1, $previous = null)
    {
        throw new HttpRequestException($msg, $code, $previous);
    }

}
